﻿using Core.Extension;
using PacketDotNet.Utils;
using PacketDotNet;
using Services.Equ.PcapCommunication.Abstract;
using SharpPcap;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace Services.Equ.PcapCommunication
{
    /// <summary>
    /// 升级设备通讯抽象类
    /// </summary>
    public class WriteFileCommunication : AbstractPcapCommunication<bool>
    {

        public FileInfo UpgradeFile { get; set; }
        private Dictionary<int, bool>? resultCache;
        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="upgradeFile">升级文件</param>
        /// <param name="destinationMac">通讯mac地址</param>
        /// <param name="password">设备密码</param>
        /// <param name="device">所选网卡</param>
        public WriteFileCommunication(FileInfo upgradeFile, PhysicalAddress destinationMac, string password, ILiveDevice device) : base(destinationMac, password, device)
        {
            UpgradeFile = upgradeFile;
            DcpType = 0x40002;
            RpcType = 1;
            OpType = 4;
            WaitMillisecond = 3000;
            ReciveBreak = 100000;
        }

        public override bool ConvertData2Result(byte[] bytes)
        {
            return bytes[59] == 0;
        }

        public override byte[] CreateBlockData()
        {
            throw new NotImplementedException();
        }
        public override bool IsResponse(byte[] bytes)
        {
            if (bytes.Length != 96)
            {
                return false;
            }
            else
            {
                return base.IsResponse(bytes);
            }
        }

        public override Task<List<bool>> GetCommunicationResult()
        {
            List<bool> r=new List<bool>();
            //return Task.Run(async () =>
            //{
            //    List<WritePartFileCommunication> communications = new List<WritePartFileCommunication>();
            //    using (var stream = UpgradeFile.OpenRead())
            //    {
            //        long byteSize = 1300;
            //        long pos = stream.Position;
            //        long streamLength = stream.Length - pos;
            //        byteSize = 1300 <= streamLength ? byteSize : streamLength;
            //        var partFile = new byte[byteSize];
            //        while (stream.Read(partFile, 0, partFile.Length) > 0)
            //        {
            //            var copy = new byte[partFile.Length];
            //            partFile.CopyTo(copy, 0);
            //            WritePartFileCommunication write = new WritePartFileCommunication(copy, DestinationMac, Password, Device);
            //            write.SeqId = SeqId;
            //            write.UUID = UUID;
            //            communications.Add(write);
            //            SeqId++;
            //        }
            //        Device.Open();
            //        foreach (var write in communications)
            //        {
            //            //var result = await RetryFunction(write.GetCommunicationResult, t => t.Any(t => t == true));
            //            var result =await write.GetCommunicationResult();
            //            if (!result.Any(t => true))
            //            {
            //                r.Add(false);
            //                break;
            //            }
            //        }
            //        Device.Close();
            //        r.Add(true);
            //        return r;
            //    }
            //});
            return Task.Run(() =>
            {
                var fileCache = new Dictionary<int, byte[]>();
                resultCache = new Dictionary<int, bool>();
                using (var stream = UpgradeFile.OpenRead())
                {
                    byte[] partFile = new byte[1300];
                    int byteReads;
                    while ((byteReads= stream.Read(partFile, 0, partFile.Length)) > 0)
                    {
                        var copy = new byte[byteReads];
                        Buffer.BlockCopy(partFile, 0, copy, 0, byteReads);
                        fileCache.Add(SeqId, copy);
                        SeqId++;
                    }
                }
                var list = new List<bool>();
                if (IsAutoControlDevice)
                {
                    Device.Open();
                    //Device.Filter = Filter;
                }
                var exitEvent = new ManualResetEvent(false);
                int i = 0;
                Device.OnPacketArrival += (sender, args) =>
                {
                    var p = args.GetPacket();
                    if (IsResponse(p.Data))
                    {
                        i++;
                        if (i >= ReciveBreak)
                        {
                            exitEvent.Set();
                        }
                        bool result = ConvertData2Result(p.Data);
                        var seqBytes = p.Data[84..88];
                        int seq = BitConverter.ToInt32(seqBytes.Reverse().ToArray());
                        if (resultCache.TryGetValue(seq, out var status))
                        {
                            if (!status)
                            {
                                resultCache[seq]=result;
                            }
                        }
                        else
                        {
                            resultCache.Add(seq, result);
                        }
                    }
                };
                Device.StartCapture();
                foreach (int key in fileCache.Keys)
                {
                    SendData(fileCache[key], key,10);
                }
                exitEvent.WaitOne(WaitMillisecond);
                int failTimes = 0;
                foreach (int key in fileCache.Keys)
                {
                    if (resultCache.TryGetValue(key, out var result))
                    {
                        if (!result)
                        {
                            SendData(fileCache[key], key,5);
                            failTimes++;
                        }
                    }
                    else
                    {
                        SendData(fileCache[key], key,5);
                        failTimes++;
                    }
                }
                exitEvent.WaitOne(failTimes * 15);
                if (IsAutoControlDevice)
                {
                    Device.StopCapture();
                    Device.Close();
                }
                foreach (int key in fileCache.Keys)
                {
                    if (resultCache.TryGetValue(key, out var result))
                    {
                        r.Add(result);
                    }
                    else
                    {
                        r.Add(false);
                    }
                }
                SeqId = fileCache.Keys.Max(x => x);
                if (r.All(t => t == true))
                {
                    return new List<bool> { true };
                }
                else
                {
                    return new List<bool> { false };
                }
            });
        }
        private bool SendData(byte[] data,int seqId,int sleepMilliseconds)
        {
            var header = GetEthernetHeader(Device.GetDeviceMacAddr()!, DestinationMac);
            SeqId = seqId;
            var ethernetPacket = new EthernetPacket(new ByteArraySegment(header));
            var blockHeader = CreateRpcBlockHeader(data.Length);
            var dataHeader = CreateRpcHeader(blockHeader.Length + data.Length);
            byte[] connectData = [.. Frame, .. dataHeader, .. blockHeader, .. data];
            ethernetPacket.PayloadData = connectData;
            Device.SendPacket(ethernetPacket);
            Thread.Sleep(sleepMilliseconds);
            if (resultCache!.TryGetValue(seqId,out var result))
            {
                return result;
            }
            return false;
        }
    }
    /// <summary>
    /// 将文件分片上传至设备
    /// </summary>
    public class WritePartFileCommunication : AbstractPcapCommunication<bool>
    {
        private byte[] partFile = [];

        public WritePartFileCommunication(byte[] partFile, PhysicalAddress destinationMac, string password, ILiveDevice device) : base(destinationMac, password, device)
        {
            IsAutoControlDevice = false;
            this.partFile = partFile;
            DcpType = 0x40002;
            RpcType = 1;
            OpType = 4;
        }

        public override async Task<List<bool>> GetCommunicationResult()
        {
            return await RetryFunction(base.GetCommunicationResult, t => t.Any(t => true));
        }
        public override byte[] CreateBlockData()
        {
            return partFile;
        }
        public override byte[] CreateBlock()
        {
            return CreateBlockData();
        }

        public override bool ConvertData2Result(byte[] bytes)
        {
            return bytes[59] == 0;
        }
        public override bool IsResponse(byte[] bytes)
        {
            if (bytes.Length != 96)
            {
                return false;
            }
            if (base.IsResponse(bytes))
            {
                var seqBytes = bytes[84..88];
                int seq = BitConverter.ToInt32(seqBytes.Reverse().ToArray());
                return seq == SeqId;
            }
            return false;
        }
    }
}
